/******************************************************************************
 * Copyright (C) 2024 EternalChip, Inc.(Gmbh) or its affiliates.
 * 
 * All Rights Reserved.
 * 
 * @file bsp_key_irq.c
 *
 * 
 * @author Jack | R&D Dept. | EternalChip 立芯嵌入式
 * 
 * @brief Provide the HAL APIs of AHT21 and corresponding opetions.
 * 
 * Processing flow:
 * 
 * call directly.
 * 
 * @version V1.0 2024-11-25
 *
 * @note 1 tab == 2 spaces!
 * 
 *****************************************************************************/

#include "bsp_key_irq.h"
#include <stdbool.h>

xQueueHandle Key_Queue       = NULL;
xQueueHandle inter_key_queue = NULL;

#define KEY_DEBOUNCE_DELAY       10     /* 按键消抖延时     */
#define KEY_LONG_PRESS_DELAY     500    /* 按键长按检测延时 */ 
#define KEY_MULTI_PRESSED_DELAY  200    /* 双击按键检测延时 */
/**
 * @brief 实现读取按键状态的代码
 * 
 * @return KEY_PRESSED 、KEY_LONGPRESSED、KEY_RELEASED、 KEY_ERROR
 */
key_press_status_t read_key() {
    static key_press_status_t key_status = KEY_RELEASED;
    static uint32_t debounce_timer       = portMAX_DELAY;
    static uint32_t long_press_timer     = portMAX_DELAY;
    static uint32_t multi_press_timer    = portMAX_DELAY;
    static uint8_t  key_press_cnt        = 0;

    key_press_status_t ret_val  = KEY_RELEASED;
    uint32_t current_key_status = HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin);

    switch (key_status)
    {
    case KEY_RELEASED:
      /* 检测按键是否被按下 */
      if ( current_key_status == KEY_PRESSED ) 
      {
        key_status = KEY_DEBOUNCE;
        debounce_timer = osKernelGetTickCount() + KEY_DEBOUNCE_DELAY;
      }
      else  /* 按键释放判断 */
      {
        if ( osKernelGetTickCount() > multi_press_timer ) {
          if ( key_press_cnt == 1 ) {
            ret_val = KEY_PRESSED;
          } else if ( key_press_cnt >= 2 ) {
            ret_val = KEY_DOUBLE_PRESSED;
          }
          key_press_cnt = 0;
          multi_press_timer = portMAX_DELAY;
          long_press_timer  = portMAX_DELAY;
        }
      }
      break;
    case KEY_LONGPRESS:
      /* 检测长按是否松开 */
      if ( current_key_status == KEY_RELEASED ) {
        multi_press_timer = portMAX_DELAY;
        long_press_timer  = portMAX_DELAY;
        key_status = KEY_RELEASED;
      }
      break;
    case KEY_DEBOUNCE:
      if ( current_key_status == KEY_PRESSED ) 
      {
        if (osKernelGetTickCount() > debounce_timer) {  
          key_status = KEY_PRESSED;
          debounce_timer = portMAX_DELAY;
          long_press_timer = osKernelGetTickCount() + KEY_LONG_PRESS_DELAY;
        }
      }
      else 
      {
        key_status = KEY_RELEASED;
      }
      break;
    case KEY_PRESSED:
      if ( current_key_status == KEY_PRESSED )
      {
        if ( osKernelGetTickCount() > long_press_timer ) {
          ret_val    = KEY_LONGPRESS;
          key_status = KEY_LONGPRESS;
        }
      }
      else 
      {
        key_press_cnt += 1;
        printf("key_press_cnt = %d\r\n", key_press_cnt);
        multi_press_timer = osKernelGetTickCount() + KEY_MULTI_PRESSED_DELAY;
        long_press_timer  = osKernelGetTickCount() + KEY_LONG_PRESS_DELAY;
        key_status = KEY_RELEASED;
      }
      break;
    default:
      break;
    }
    return ret_val;
}


key_status_t key_scan(key_press_status_t* key_value) 
{
  uint32_t timeoutTicks = 0;
  key_press_status_t key_status = KEY_RELEASED;

  while (timeoutTicks++ < 1000) 
  {
    switch ( read_key() )
    {
    case KEY_PRESSED:
      key_status = KEY_PRESSED;
      *key_value = key_status;
      return KEY_OK;  
    case KEY_LONGPRESS:
      key_status = KEY_LONGPRESS;
      *key_value = key_status;
      return KEY_OK;
    case KEY_DOUBLE_PRESSED:
      printf("key_double_pressed\r\n");
      key_status = KEY_DOUBLE_PRESSED;
      *key_value = key_status;
      return KEY_OK;
    default:
      break;
    }
  }
  *key_value = key_status;
  return KEY_ERRORTIMEOUT;
}


/**
 * @brief 键盘任务函数，负责扫描键盘状态并处理按键事件
 * 
 * 本函数周期性地扫描键盘，检查键盘是否被按下。如果检测到按键事件，
 * 则通过队列发送按键状态。这是通过调用key_scan函数来完成的，如果键被按下，
 * 则向Key_Queue队列发送按键状态，通知其他任务或模块按键事件已发生。
 * 
 * @param argument 任务参数，未使用
 */
void Key_Task(void *argument) 
{
  /** Variables (in task stack)                                 **/
  // key_status_t       key_ret             = KEY_OK;
  // key_press_status_t key_status          = KEY_RELEASED;
  uint32_t           event_idx           = 0;
  uint32_t           first_trigger_tick  = 0;
  uint32_t           short_press_time    = SHORT_PRESS_TIME;
  key_press_event_t  key_event           = {KEY_TRIGGER_RISING_EDGE, 0};
  key_press_event_t* p_key_event         = NULL;
  /** Variables (in task stack)                                 **/

  /** Variables (in OS heap)                                    **/
  Key_Queue       = xQueueCreate(10, sizeof(key_press_status_t));
  // inter_key_queue = xQueueCreate(10, sizeof(key_press_event_t));
  inter_key_queue = xQueueCreate(10, 4);
  /** Variables (in OS heap)                                    **/

  if ( NULL == Key_Queue      || 
       NULL == inter_key_queue ) 
  {
    printf("Key_Queue Create Failed!\r\n");
    return ;
  } else {
    printf("Key_Queue Create OK!\r\n");
  }

  for(;;)
  {
    //printf("Key_Task Running[%d]! \r\n", HAL_GetTick());
    // if ( pdTRUE == xQueueReceive(inter_key_queue, &key_event,  ( TickType_t ) 0) )
    if ( pdTRUE == xQueueReceive(inter_key_queue, &p_key_event,  ( TickType_t ) 0) )
    {
      printf("inter_key_queue receive trigger_tick = [%d] "
             "at [%d] tick \r\n", 
                                  key_event.trigger_tick, 
                                  HAL_GetTick());

      if ( NULL == p_key_event ) {
        continue;
      }

      key_event.edge_type    = p_key_event->edge_type;
      key_event.trigger_tick = p_key_event->trigger_tick;

      switch (event_idx)
      {
      case 0:
      {
        if ( KEY_TRIGGER_RISING_EDGE == key_event.edge_type )
        {
          printf("key rising fetch error! at [%d] tick \r\n", HAL_GetTick());
        }

        if ( KEY_TRIGGER_FALLING_EDGE == key_event.edge_type )
        {
          printf("key first falling edge trigger_tick = [%d]"
                 "at [%d] tick \r\n", 
                                      key_event.trigger_tick,
                                      HAL_GetTick() );
          event_idx += 1;
          first_trigger_tick = key_event.trigger_tick;
        }
        break;
      }
      case 1:
      {
        if ( KEY_TRIGGER_RISING_EDGE == key_event.edge_type ) 
        {
          printf("key rising after the falling edge");
          if ( key_event.trigger_tick - first_trigger_tick < 10) {
            printf("invalid key fetched at [%d] tick", HAL_GetTick());
            break;
          }
          
          /* 短按判断 */
          if ( key_event.trigger_tick - first_trigger_tick < short_press_time ) 
          {
            key_press_status_t key_result = KEY_PRESSED;

            if ( pdTRUE == xQueueSend(Key_Queue, &key_result, 0) ) 
            {
              printf("Key Queue Send PRESSED at [%d] tick \r\n", HAL_GetTick());
              event_idx = 0;
            }
            else{
              printf("Key Queue Send Failed! at [%d] tick \r\n", HAL_GetTick());
            }
          }

          /* 长按判断 */
          if ( key_event.trigger_tick - first_trigger_tick > short_press_time ) 
          {
            key_press_status_t key_result = KEY_LONGPRESS;

            if ( pdTRUE == xQueueSend(Key_Queue, &key_result, 0) ) 
            {
              printf("Key Queue Send LONGPRESSED at [%d] tick \r\n", HAL_GetTick());
              event_idx = 0;
            }
            else
            {
              printf("Key Queue Send Failed! at [%d] tick \r\n", HAL_GetTick());
            }
          }
        }
        break;
      }
      default:
        break;
      }
    }
    osDelay(100);
  }
}

/**
 * @brief key_interrupt_callback函数，用于处理键盘中断
 * 
 */
KEY_IRQ_CALLBACK
{
  HAL_GPIO_WritePin(IRQ_TRACE_GPIO_Port, IRQ_TRACE_Pin, GPIO_PIN_SET);
  static key_trigger_edge_t irq_edge_type = KEY_TRIGGER_FALLING_EDGE;
  static uint32_t       last_trigger_tick = 0;
  static key_press_event_t    key_event_1 = {KEY_TRIGGER_FALLING_EDGE, 0};
  static key_press_event_t* p_key_event_1 = &key_event_1;
  static key_press_event_t    key_event_2 = {KEY_TRIGGER_RISING_EDGE, 0};
  static key_press_event_t* p_key_event_2 = &key_event_2;
  GPIO_InitTypeDef        GPIO_InitStruct = {0};
  uint32_t                   current_tick = HAL_GetTick();


  if (current_tick - last_trigger_tick < KEY_DEBOUNCE_DELAY) {
    return;
  }

  switch ( irq_edge_type )
  {
  case KEY_TRIGGER_FALLING_EDGE:
  {
    // key_event_1.edge_type    = KEY_TRIGGER_FALLING_EDGE;
    key_event_1.trigger_tick = HAL_GetTick();

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    if ( pdPASS == xQueueSendFromISR(inter_key_queue, &p_key_event_1, &xHigherPriorityTaskWoken) ) 
    // if ( pdPASS == xQueueSendFromISR(inter_key_queue, &key_event_1, &xHigherPriorityTaskWoken) ) 
    {
      printf("send FALLING to inter_key_queue at [%d] tick\r\n", HAL_GetTick());
    }

    GPIO_InitStruct.Pin  = KEY_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);

    irq_edge_type = KEY_TRIGGER_RISING_EDGE;

    if (  xHigherPriorityTaskWoken ) {
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }

    last_trigger_tick = current_tick;
    break;
  }
  case KEY_TRIGGER_RISING_EDGE:
  {
    // key_event_2.edge_type    = KEY_TRIGGER_RISING_EDGE;
    key_event_2.trigger_tick = HAL_GetTick();

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    if ( pdPASS == xQueueSendFromISR(inter_key_queue, &p_key_event_2, &xHigherPriorityTaskWoken) ) 
    // if ( pdPASS == xQueueSendFromISR(inter_key_queue, &key_event_2, &xHigherPriorityTaskWoken) ) 
    {
      printf("send RISING to inter_key_queue at [%d] tick\r\n", HAL_GetTick());
    }
    GPIO_InitStruct.Pin  = KEY_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);

    irq_edge_type = KEY_TRIGGER_FALLING_EDGE;

    if (  xHigherPriorityTaskWoken ) {
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }

    last_trigger_tick = current_tick;
    break;
  }
  default:
    break;
  }
  HAL_GPIO_WritePin(IRQ_TRACE_GPIO_Port, IRQ_TRACE_Pin, GPIO_PIN_RESET);

}

